package com.yugao.fintech.draper.datascope.aspect;

import com.yugao.fintech.draper.datascope.annotation.DataScope;
import com.yugao.fintech.draper.datascope.handler.DataScopeContextHolder;
import lombok.AllArgsConstructor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义Advice 处理数据权限的通知
 * <p>
 * 1. 在执行方法前 将数据权限注解入栈 2. 在执行方法后 将数据权限注解出栈
 * </p>
 */
@DataScope
@AllArgsConstructor(staticName = "of")
public class DataScopeCustomAdvice implements MethodInterceptor {
	/**
	 * 方法无数据权限注解时 使用此进行占位
	 */
	static final DataScope DATA_PERMISSION_NULL = DataScopeCustomAdvice.class.getAnnotation(DataScope.class);

	private final Map<MethodClassKey, DataScope> dataPermissionCache = new ConcurrentHashMap<>();

	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		// 方法执行前 获取方法上的数据权限注解
		DataScope dataScope = this.findAnnotation(methodInvocation);
		if (Objects.nonNull(dataScope)) {
			// 数据权限注解入栈
			DataScopeContextHolder.add(dataScope);
		}
		try {
			// 执行目标方法
			return methodInvocation.proceed();
		}
		finally {
			if (Objects.nonNull(dataScope)) {
				// 方法执行后 数据权限注解出栈
				DataScopeContextHolder.remove();
			}
		}
	}

	private DataScope findAnnotation(MethodInvocation methodInvocation) {
		Method method = methodInvocation.getMethod();
		Object targetObject = methodInvocation.getThis();
		Class<?> clazz = Objects.nonNull(targetObject) ? targetObject.getClass() : method.getDeclaringClass();
		MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
		// 从缓存中获取数据权限注解
		DataScope dataScope = dataPermissionCache.get(methodClassKey);
		if (Objects.nonNull(dataScope)) {
			// 缓存中已经存在此方法的数据权限信息 直接返回
			return dataScope != DATA_PERMISSION_NULL ? dataScope : null;
		}
		// 优先从方法中获取注解
		dataScope = AnnotationUtils.findAnnotation(method, DataScope.class);
		if (Objects.isNull(dataScope)) {
			// 方法上没有 从类上获取
			dataScope = AnnotationUtils.findAnnotation(clazz, DataScope.class);
		}
		// 方法或者类上有注解则添加到缓存中 没有则添加默认的到缓存
		dataPermissionCache.put(methodClassKey, Objects.nonNull(dataScope) ? dataScope : DATA_PERMISSION_NULL);
		return dataScope;
	}
}
